﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// Pour en savoir plus sur le modèle d'élément Contrôle utilisateur, consultez la page http://go.microsoft.com/fwlink/?LinkId=234236

namespace BlipSansBlop
{
    public enum EtatPerso { Marche = 1, Attente = 2, Monte = 3, Tombe = 4, Plane = 5 };
    public sealed partial class Perso : UserControl
    {
        // Etapes d'un saut
        public static double[] EtapesSaut = new double[] { 
            (0),
            (-4) * MainPage.Gravite ,
            (-4) * MainPage.Gravite ,
            (-3) * MainPage.Gravite ,
            (-3) * MainPage.Gravite ,
            (-2) * MainPage.Gravite ,
            (-2) * MainPage.Gravite ,
            (-2) * MainPage.Gravite ,
            (-2) * MainPage.Gravite ,
            (-1.5) * MainPage.Gravite  ,
            (-1.5) * MainPage.Gravite  ,
            (-1.5) * MainPage.Gravite  ,
            (-1.2) * MainPage.Gravite  ,
            (-1.2) * MainPage.Gravite  ,
            (-1.2) * MainPage.Gravite  ,
            (-1.02) * MainPage.Gravite  ,
            (-1.02) * MainPage.Gravite  ,
            (-1.02) * MainPage.Gravite  ,
            (-0.98) * MainPage.Gravite  ,
            (-0.98) * MainPage.Gravite  ,
            (-0.98) * MainPage.Gravite  ,
            (-0.8) * MainPage.Gravite  ,
            (-0.8) * MainPage.Gravite  ,
            (-0.8) * MainPage.Gravite  ,
            (-0.5) * MainPage.Gravite  ,
            (-0.5) * MainPage.Gravite  ,
            (-0.5) * MainPage.Gravite
        };

        public static double[] EtapesBumper = new double[] { 
            (0),
            (-8) * MainPage.Gravite ,
            (-8) * MainPage.Gravite ,
            (-8) * MainPage.Gravite ,
            (-8) * MainPage.Gravite ,
            (-8) * MainPage.Gravite ,
            (-8) * MainPage.Gravite ,
            (-4) * MainPage.Gravite  ,
            (-4) * MainPage.Gravite  ,
            (-4) * MainPage.Gravite  ,
            (-4) * MainPage.Gravite  ,
            (-2) * MainPage.Gravite  ,
            (-2) * MainPage.Gravite  ,
            (-1.728) * MainPage.Gravite  ,
            (-1.728) * MainPage.Gravite  ,
            (-1.06) * MainPage.Gravite  ,
            (-1.06) * MainPage.Gravite  ,
            (-1.06) * MainPage.Gravite  ,
            (-1.02) * MainPage.Gravite  ,
            (-1.02) * MainPage.Gravite  ,
            (-1.02) * MainPage.Gravite  ,
            (-1.02) * MainPage.Gravite  ,
            (-1.02) * MainPage.Gravite  ,
            (-1.02) * MainPage.Gravite  ,
            (-0.94) * MainPage.Gravite  ,
            (-0.94) * MainPage.Gravite  ,
            (-0.94) * MainPage.Gravite  ,
            (-0.512) * MainPage.Gravite  ,
            (-0.512) * MainPage.Gravite  ,
            (-0.512) * MainPage.Gravite  ,
            (-0.125) * MainPage.Gravite  ,
            (-0.125) * MainPage.Gravite  ,
            (-0.125) * MainPage.Gravite
        };
        // Pour en faire un singleton
        public static Perso Instance { get; set; }

        // Position du joueur
        public static Point Centre { get; set; }

        public static double Angle { get; set; }
        // Etape actuelle du saut
        public static int Saut { get; set; }
        public static long Balles { get; set; }
        // Etape actuelle du saut
        public static int Bumper { get; set; }

        public static int CompteurVie { get; set; }
        public static int Vie { get; set; } // Max = 6

        public static EtatPerso Etat { get; set; }
        public static double Vitesse { get; set; }

        public static CompositeTransform BlipComposite { get; set; }
        public static CompositeTransform UserControlComposite { get; set; }

        public Perso()
        {
            this.InitializeComponent();
            Perso.Instance = this;
            Perso.Centre = new Point(Canvas.GetLeft(Perso.Instance), Canvas.GetTop(Perso.Instance));
            Perso.Vitesse = 6;
            Perso.Saut = 0;
            Perso.Bumper = 0;
            Perso.Balles = 500;
            Perso.CompteurVie = 60;
            Perso.Instance.Opacity = 0.2;
            Perso.Vie = 6;
            Perso.Etat = EtatPerso.Attente;
            Perso.BlipComposite = new CompositeTransform();
            TransformGroup BlipTransformGroup = new TransformGroup();
            BlipTransformGroup.Children.Add(Perso.BlipComposite);
            Perso.Instance.Blip.RenderTransform = BlipTransformGroup;

            Perso.UserControlComposite = Perso.Instance.PersoUserControl;
        }

        /// <summary>
        /// Gestion du sprite à afficher en fonction de la position et de l'état du perso
        /// </summary>
        public static void SwitchToEtat()
        {
            switch (Perso.Etat)
            {
                case EtatPerso.Marche:
                    Perso.BlipComposite.TranslateY = 0;
                    if (Perso.BlipComposite.TranslateX <= -560) Perso.BlipComposite.TranslateX = 0;
                    else Perso.BlipComposite.TranslateX -= 40;
                    break;
                case EtatPerso.Attente:
                    Perso.BlipComposite.TranslateY = (-55);
                    // Clignement des yeux à un moment aléatoire
                    if (Perso.BlipComposite.TranslateX <= -280)
                    {
                        if (MainPage.rand.NextDouble() > 0.98) Perso.BlipComposite.TranslateX = 0;
                        else Perso.BlipComposite.TranslateX = (-280);
                    }
                    else Perso.BlipComposite.TranslateX -= 40;
                    break;
                case EtatPerso.Tombe:
                    Perso.BlipComposite.TranslateY = (-110);
                    if (Perso.BlipComposite.TranslateX <= -120) Perso.BlipComposite.TranslateX = 0;
                    else Perso.BlipComposite.TranslateX -= 40;
                    break;
                case EtatPerso.Plane:
                    Perso.BlipComposite.TranslateY = (-165);
                    if (Perso.BlipComposite.TranslateX <= -80) Perso.BlipComposite.TranslateX = 0;
                    else Perso.BlipComposite.TranslateX -= 40;
                    break;
                case EtatPerso.Monte:
                    Perso.BlipComposite.TranslateY = (-220);
                    if (Perso.BlipComposite.TranslateX <= -120) Perso.BlipComposite.TranslateX = 0;
                    else Perso.BlipComposite.TranslateX -= 40;
                    break;
            }
        }

        /// <summary>
        /// Attribution de l'état correspondant au déplacements du perso
        /// </summary>
        /// <param name="Mouvement">Déplacement récupéré</param>
        public static void Image_suivante(Point Mouvement)
        {
            EtatPerso Save = Perso.Etat;
            // Symétrie horizontale
            if (Mouvement.X != 0)
            {
                if (Mouvement.X > 0) Perso.UserControlComposite.ScaleX = (-1);
                else if (Mouvement.X < 0) Perso.UserControlComposite.ScaleX = 1;
            }

            if (Mouvement.Y != 0)
            { // Deplacement Vertical
                if (Mouvement.Y > 1) //Tombe
                {
                    Perso.Etat = EtatPerso.Tombe;
                }
                else if (Mouvement.Y <= 1 && Mouvement.Y >= (-1)) // Plane
                {
                    Perso.Etat = EtatPerso.Plane;
                }
                else if (Mouvement.Y < (-1)) // Monte
                {
                    Perso.Etat = EtatPerso.Monte;
                }
            }
            // Si ne monte pas ni ne descend mais bouge
            else if (Mouvement.X != 0) Perso.Etat = EtatPerso.Marche;
            // Si ne se déplace pas horizontalement non plus ...
            else Perso.Etat = EtatPerso.Attente;
            // Si changement d'état, réinitialisation du calque d'animations
            if (Save != Perso.Etat) Perso.BlipComposite.TranslateX = 0;
            // Changement de l'affichage en fonction de l'état du joueur
            Perso.SwitchToEtat();
        }

        public static void Hit()
        {
            if (Perso.CompteurVie == 0)
            {
                Perso.Instance.Opacity = 0.2;
                Perso.CompteurVie = 60;
                Perso.Vie--;
            }

            if (Perso.Vie == 0) Perso.Kill();
        }

        /// <summary>
        /// Se lance à chaque tick de jeu
        /// </summary>
        public static void Update()
        {
            if (Perso.CompteurVie > 0) Perso.CompteurVie--;
            else Perso.Instance.Opacity = 1;
            if (HitTest(0, 0, MainPage.Ennemis)) Perso.Hit();


            Perso.Centre = new Point(Canvas.GetLeft(Perso.Instance), Canvas.GetTop(Perso.Instance));

            if (Joystick.PositionX != 0 || Joystick.PositionY != 0) Perso.Angle = Joystick.AngleEnDegre;
            if (BoutonA.EstAppuie && Perso.Balles!=0)
            {
                Perso.Balles--;
                new Tir(new Point(Perso.Centre.X + 12, Perso.Centre.Y + 34), Perso.Angle);
            }
            // Gestion du saut
            if (Perso.Saut != 0 && Perso.Bumper==0)
            {
                if (Perso.Saut < Perso.EtapesSaut.Count() - 1) Perso.Saut++;
                else Perso.Saut = 0;

            }
            if (Perso.Bumper != 0)
            {
                Perso.Saut = 0;
                if (Perso.Bumper < Perso.EtapesBumper.Count() - 1) Perso.Bumper++;
                else Perso.Bumper = 0;
            }
            if (Perso.Etat == EtatPerso.Attente || Perso.Etat == EtatPerso.Marche){

                if (Perso.HitTest(0, 0, MainPage.Bumpers))
                {
                    Perso.Bumper = 1;
                }
                if (BoutonB.EstAppuie) Perso.Saut = 1;
            }

            Point Deplacement = new Point(Joystick.RapportPositionX * Perso.Vitesse, MainPage.Gravite + Joystick.RapportPositionY * Perso.Vitesse + EtapesSaut[Perso.Saut] + EtapesBumper[Perso.Bumper]);

            if (!Perso.HitTest(Deplacement.X, 0, MainPage.Echelles))
            {
                Deplacement.Y -= Joystick.RapportPositionY * Perso.Vitesse;
            }
            else
            {
                Deplacement.Y -= MainPage.Gravite;
            }

            if (Perso.HitTest(Deplacement.X, Deplacement.Y, MainPage.Obstacles))
            {
                // Test du déplacement en X
                if (Perso.HitTest(Deplacement.X, 0, MainPage.Obstacles))
                {
                    Deplacement.X = 0;
                }
                // Test du déplacement en Y
                if (Perso.HitTest(0, Deplacement.Y, MainPage.Obstacles))
                {
                    Deplacement.Y = 0;
                    Perso.Bumper = 0;
                    Perso.Saut = 0;
                }
                if (Perso.HitTest(Deplacement.X, Deplacement.Y, MainPage.Obstacles))
                {
                    Deplacement.X = 0;
                    Deplacement.Y = 0;
                }
            }

            // Changement de Perso.Etat en fonction du déplacement calculé ci dessus
            Perso.Image_suivante(Deplacement);
            // Application du déplacement aux coordonnées du perso
            Perso.Centre = new Point(Perso.Centre.X + Deplacement.X, Perso.Centre.Y + Deplacement.Y);
            Canvas.SetLeft(Perso.Instance, Perso.Centre.X);
            Canvas.SetTop(Perso.Instance, Perso.Centre.Y);

        }



        /// <summary>
        /// Permet de tester la présence d'un type d'élément à l'emplacement du joueur avec un décalage de ...
        /// </summary>
        /// <param name="Decalage"></param>
        /// <param name="DossierParent">Grid de l'arboressence contenant les éléments à tester</param>
        /// <returns>Liste d'éléments présents dans cette zone</returns>
        public static Boolean HitTest(Point Decalage, UIElement DossierParent)
        {
            Point test = new Point(Perso.Centre.X + Decalage.X, Perso.Centre.Y + Decalage.Y);

            for (int x = Convert.ToInt32(Perso.Centre.X + Decalage.X) + 4; x < Convert.ToInt32(Perso.Centre.X + Decalage.X + Perso.Instance.Width) - 4; x += 2)
            {
                test.X = x;
                for (int y = Convert.ToInt32(Perso.Centre.Y + Decalage.Y) + 30; y < Convert.ToInt32(Perso.Centre.Y + Decalage.Y + Perso.Instance.Height) - 2; y += 2)
                {
                    test.Y = y;
                    if (VisualTreeHelper.FindElementsInHostCoordinates(test, DossierParent).Count() != 0) return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Permet de tester la présence d'un type d'élément à l'emplacement du joueur avec un décalage de ...
        /// </summary>
        /// <param name="DecalageX">Décalage horizontal</param>
        /// <param name="DecalageY">Décalage vertical</param>
        /// <param name="DossierParent">Grid de l'arboressence contenant les éléments à tester</param>
        /// <returns>Liste d'éléments présents dans cette zone</returns>
        public static Boolean HitTest(double DecalageX, double DecalageY, UIElement DossierParent)
        {
            Point test = new Point(Perso.Centre.X + DecalageX, Perso.Centre.Y + DecalageY);
            for (int x = Convert.ToInt32(Perso.Centre.X + DecalageX) + 12; x < Convert.ToInt32(Perso.Centre.X + DecalageX + Perso.Instance.Width) - 12; x += 2)
            {
                test.X = x;
                for (int y = Convert.ToInt32(Perso.Centre.Y + DecalageY) + 30; y < Convert.ToInt32(Perso.Centre.Y + DecalageY + Perso.Instance.Height) - 2; y += 2)
                {
                    test.Y = y;
                    if (VisualTreeHelper.FindElementsInHostCoordinates(test, DossierParent).Count() > 0) return true;
                }
            }
            return false;
        }

        public static void Kill()
        {
            var rootFrame = new Frame();
            if (!rootFrame.Navigate(typeof(Lose)))
            {
                throw new Exception("Failed to create GoBack page");
            }
            Window.Current.Content = rootFrame;
            Window.Current.Activate();
        }
    }
}
